package com.negative.data;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.HttpsURLConnection;

import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.util.Log;

import com.negative.activities.PaymentFormActivity;
import com.negative.activities.R;
import com.negative.logic.AsyncResponse;
import com.negative.logic.DialogManager;

public class PaymentFormData implements AsyncResponse{
	private PersonalDetails userDetailes;
	private OrderDetails orderDetailes;
	private OrderTicketDetails ticketDetails;
	private PaymentDetails paymentDetails;
	private Elements allInputs;
	private Elements allLabelsInForm;
	private Document doc ;
	private String pesonalDetailsDisclaimer;
	private PaymentFormActivity retActivity;

	public PaymentFormData(Document doc){
		this.doc = doc;
		allInputs = doc.select("input");
		allLabelsInForm = doc.select("#order_form label");
		pesonalDetailsDisclaimer = doc.getElementById("personal_disclaimer").text();
		userDetailes = extractPersonalDetails();
		orderDetailes = extractOrderDetails();
		ticketDetails = extractTicketDetails();
		paymentDetails = extractPaymentDetails();

	}

	private PaymentDetails extractPaymentDetails() {
		String creditNumberLabel = getLabelFor("payment_method_credit_card_credit_number");
		String creditNumberKey = getKeyFor("payment_method_credit_card_credit_number");
		String owenrIdLabel = getLabelFor("payment_method_credit_card_owner_id");
		String owenrIdkey = getKeyFor("payment_method_credit_card_owner_id");
		String ownerNameLabel = getLabelFor("payment_method_credit_card_owner_name");
		String ownerNameKey = getKeyFor("payment_method_credit_card_owner_name");
		String expirationYearLabel = getLabelFor("payment_method_credit_card_expiration");
		String expirationYearKey = "";
		String expirationMonthKey ="";
		try{
			expirationYearKey = doc.select("#payment_method_credit_card_expiration_year").first().attr("name") ;
		}catch (Exception e){}

		try {
			expirationMonthKey =  doc.select("#payment_method_credit_card_expiration_month").first().attr("name");
		} catch (Exception e){}
		String cvvLabel = getLabelFor("payment_method_credit_card_cvv");
		String cvvKey = getKeyFor("payment_method_credit_card_cvv");
		KeyValueLabel creditNumber = new KeyValueLabel(creditNumberKey, creditNumberLabel);
		KeyValueLabel owenrId = new KeyValueLabel(owenrIdkey, owenrIdLabel);
		KeyValueLabel ownerName = new KeyValueLabel(ownerNameKey, ownerNameLabel);
		KeyValueLabel expirationYear = new KeyValueLabel(expirationYearKey, expirationYearLabel);
		KeyValueLabel expirationMonth = new KeyValueLabel(expirationMonthKey, expirationYearLabel);
		KeyValueLabel cvv = new KeyValueLabel(cvvKey, cvvLabel);
		PaymentDetails ans = new PaymentDetails(creditNumber, owenrId, ownerName, expirationYear, expirationMonth, cvv);
		return ans;
	}

	private OrderTicketDetails extractTicketDetails() {
		String header = doc.select("#tickets_details h1").first().text();
		String totalPrice = doc.select("#tickets_details tfoot h2").text();
		OrderTicketDetails ans = new OrderTicketDetails(header, totalPrice);
		Elements ticketRow = doc.select("#tickets_details table tbody tr");
		for (Element row : ticketRow) {
			try {
				Elements columns = row.select("td");
				String amount = columns.get(0).text();
				String type = columns.get(1).text();
				String price = columns.get(2).text();
				String rowTotal = columns.get(3).text();
				ans.getTicketTypesData().add(new OrderTicketDetailsData(amount, type, price, rowTotal));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		return ans;
	}

	private OrderDetails extractOrderDetails() {
		String orderHeader = doc.select("#order_details h1").first().text();
		Elements tbody = doc.select("#order_details tbody td");
		String eventName = tbody.get(0).text();
		String location = tbody.get(1).text();
		String time = tbody.get(2).text();
		return new OrderDetails(orderHeader, eventName, location, time);
	}

	private PersonalDetails extractPersonalDetails() {
		String emailLabel = getLabelFor("ticket_purchase_email");
		String emailKey = getKeyFor("ticket_purchase_email");
		String firstNameLabel = getLabelFor("ticket_purchase_firstname");
		String firstNameKey = getKeyFor("ticket_purchase_firstname");
		String lastNameLabel = getLabelFor("ticket_purchase_lastname");
		String lastNameKey = getKeyFor("ticket_purchase_lastname");
		String idLabel = getLabelFor("ticket_purchase_id_number");
		String idKey = getKeyFor("ticket_purchase_id_number");
		String phoneNumberLabel = getLabelFor("ticket_purchase_phone_number");
		String phoneNumberNumberKey = getKeyFor("ticket_purchase_phone_number_number");
		String phoneNumberAreaLabel = null;
		String phoneNumberAreaKey = doc.getElementById("ticket_purchase_phone_number_area").attr("name");
		PersonalDetails ans =
				new PersonalDetails(emailLabel, emailKey,
						firstNameLabel, firstNameKey,
						lastNameLabel, lastNameKey, 
						idLabel, idKey, 
						phoneNumberLabel, phoneNumberNumberKey,
						phoneNumberAreaLabel, phoneNumberAreaKey);
		return ans;
	}

	private String getKeyFor(String id) {
		Elements select = allInputs.select("#"+id);
		String ans = "";
		try {
			ans = select.first().attr("name");
		} catch (Exception e){}
		return ans;
	}

	private String getLabelFor(String forAttr) {
		Elements select = allLabelsInForm.select("[for="+forAttr+"]");
		String ans = "";
		try {
			ans = select.first().text();
		} catch (Exception e) {}
		return ans;
	}

	public PersonalDetails getUserDetailes() {
		return userDetailes;
	}

	public OrderDetails getOrderDetailes() {
		return orderDetailes;
	}

	public OrderTicketDetails getTicketDetails() {
		return ticketDetails;
	}

	public PaymentDetails getPaymentDetails() {
		return paymentDetails;
	}

	public Elements getAllInputs() {
		return allInputs;
	}

	public Elements getAllLabelsInPersonalForm() {
		return allLabelsInForm;
	}

	public Document getDoc() {
		return doc;
	}

	public String getPesonalDetailsDisclaimer() {
		return pesonalDetailsDisclaimer;
	}

	public void sendToServer(PaymentFormActivity ret){
		SendPurchaseDataTask task = new SendPurchaseDataTask();
		task.delegate = this ;  
		this.retActivity = ret;
		task.execute();

	}


	@Override
	public void processFinish(String result) {
		if (result != null){
			Document doc = Jsoup.parse(result);
			boolean hasErrors = doc.select("#purchase_success").isEmpty();
			if (hasErrors){
				returnErrorsToPurchaseActivity(doc);
			}else{
				createETicket(doc);
			}
		}else{
			DialogManager.showOKDialog(this.retActivity,
					true,
					retActivity.getString(R.string.error), 
					retActivity.getString(R.string.connection_to_server_failed));
		}
	}

	private void createETicket(Document doc) {
		Date purchaseDate = Calendar.getInstance().getTime();
		PurchaseData data = null;
		Element successDiv = doc.getElementById("header_information");
		String successMessage = successDiv.text();
		try{
			Pattern p = Pattern.compile("\\d{6}");
			Matcher m = p.matcher(successMessage);
			String orderNumber = successMessage.substring(m.start(), m.end());
			data = new PurchaseData(orderNumber,orderDetailes.getEventName() ,userDetailes.getId().getValue(), 
					purchaseDate, orderDetailes.getStartTime());


		}catch (Exception e){
			int start = successMessage.indexOf(":");
			if (start != -1){
				start +=2 ;
				int end = successMessage.indexOf(" ",start);
				if (end != -1){
					String orderNumber = successMessage.substring(start,end);
					data = new PurchaseData(orderNumber,orderDetailes.getEventName() ,userDetailes.getId().getValue(), 
							purchaseDate, orderDetailes.getStartTime());
				}
				else{
					data = new PurchaseData("", orderDetailes.getEventName(), 
							userDetailes.getId().getValue(), purchaseDate, orderDetailes.getStartTime());
				}
			}else{
				data = new PurchaseData("", orderDetailes.getEventName(), 
						userDetailes.getId().getValue(), purchaseDate, orderDetailes.getStartTime());
			}

		}finally{
			retActivity.createEtickect(data);
		}
	}


	private void returnErrorsToPurchaseActivity(Document doc) {
		HashMap<String, String> errors = new HashMap<String, String>();
		Elements tableCells = doc.select("#order_form_right tbody tr");
		tableCells.addAll(doc.select("#order_form_left tbody tr"));
		for (Element cell : tableCells) {
			if (!cell.select(".error_list").isEmpty()){
				String key = cell.select("label").first().ownText();
				String error = cell.select(".error_list").text();
				errors.put(key, error);

			}
		}
		retActivity.returnWithErrors(errors);
	}

	private class SendPurchaseDataTask extends AsyncTask<Void, Void, String>{

		public AsyncResponse delegate;
		ProgressDialog mProgressDialog;

		private NameValuePair createNameValueCsrf() {
			Element csrfInput = allInputs.select("#sfApplyApply__csrf_token").first();
			String csrfKey = csrfInput.attr("name");
			String csrfValue =  csrfInput.attr("value");
			return new BasicNameValuePair(csrfKey, csrfValue);
		}

		private NameValuePair createNameValue(KeyValueLabel kvl) {

			return new BasicNameValuePair(kvl.getKey(), kvl.getValue());
		}


		@Override
		protected String doInBackground(Void... params) {
			HttpsURLConnection connection = null;
			String ans = null;
			String payloadString = getPayloadAsString();
			try {
				URL url = new URL( doc.baseUri());
				connection = (HttpsURLConnection) url.openConnection();	
				connection.setRequestMethod("POST");
				BufferedWriter payload = new BufferedWriter(
						new OutputStreamWriter(connection.getOutputStream(),"UTF-8"));
				payload.write(payloadString);
				payload.flush();
				connection.connect();
				int status = connection.getResponseCode();
				InputStreamReader response;
				if (status >= HttpStatus.SC_BAD_REQUEST){
					Log.e("Error", "connection to server failed status: " + status );
					return null;
				}else{
					response = new InputStreamReader(connection.getInputStream());
				}
				ans = readResponse(response);
				payload.close();
				response.close();

			} catch (MalformedURLException e) {
				return null;
			} catch (IOException e) {
				return null;
			}finally{
				if(connection != null)
					connection.disconnect();
			}
			return ans;
		}

		private String getPayloadAsString() {
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			nvps.add(createNameValue(userDetailes.getEmail()));
			nvps.add(createNameValue(userDetailes.getFirstName()));
			nvps.add(createNameValue(userDetailes.getLastName()));
			nvps.add(createNameValue(userDetailes.getId()));
			nvps.add(createNameValue(userDetailes.getPhoneNumberArea()));
			nvps.add(createNameValue(userDetailes.getPhoneNumberNumber()));
			nvps.add(createNameValueCsrf());
			nvps.add(new BasicNameValuePair("method", "PaymentMethodCreditCard"));
			nvps.add(createNameValue(paymentDetails.getCreditNumber()));
			nvps.add(createNameValue(paymentDetails.getOwenrId()));
			nvps.add(createNameValue(paymentDetails.getOwnerName()));
			nvps.add(createNameValue(paymentDetails.getExpirationMonth()));
			nvps.add(createNameValue(paymentDetails.getExpirationYear()));
			nvps.add(createNameValue(paymentDetails.getCvv()));
			nvps.add(new BasicNameValuePair("ticket_purchase[terms_and_services]", "on"));
			try {
				return getQuery(nvps);
			} catch (UnsupportedEncodingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return "";
		}

		private String readResponse(InputStreamReader response) throws IOException {
			StringBuffer text = new StringBuffer();
			BufferedReader buff = new BufferedReader(response);
			String line = buff.readLine();
			while(line != null ){
				text.append(line);
				line = buff.readLine();
			}
			return text.toString();


		}

		private String getQuery(List<NameValuePair> params) throws UnsupportedEncodingException
		{
			StringBuilder result = new StringBuilder();
			boolean first = true;

			for (NameValuePair pair : params)
			{
				if(pair.getValue() == null){
					continue;
				}
				if (first)
					first = false;
				else
					result.append("&");

				result.append(URLEncoder.encode(pair.getName(), "UTF-8"));
				result.append("=");
				result.append(URLEncoder.encode(pair.getValue(), "UTF-8"));
			}

			return result.toString();
		}
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			mProgressDialog = new ProgressDialog(retActivity);
			mProgressDialog.setTitle(retActivity.getString(R.string.negative_plus));
			mProgressDialog.setMessage(retActivity.getString(R.string.loading));
			mProgressDialog.setIcon(R.drawable.negative_plus_icon);
			mProgressDialog.setCanceledOnTouchOutside(false);
			mProgressDialog.show();
		}

		@Override
		protected void onPostExecute(String result) {
			super.onPostExecute(result);
			delegate.processFinish(result);
			mProgressDialog.dismiss();
			//			DialogManager.showOKDialog(retActivity, true, retActivity.getString(R.string.info), retActivity.getString(R.string.purchase_completed));;
		}

	}
}
